import type { AsyncCallback, AsyncTask } from "../types";

class AsyncQueue {
  private _runnungAsyncTask: AsyncTask | null = null;
  private _queues: Array<AsyncTask> = [];
  private static _$uuid_count: number = 1;
  private _isProcessingTaskUUID: number = 0;
  private _enable: boolean = true;

  get queues() {
    return this._queues;
  }

  get isProcessing() {
    return this._isProcessingTaskUUID > 0;
  }

  complete: Function | null = null;

  constructor() {}

  push(callback: AsyncCallback, params: any = null) {
    const uuid = AsyncQueue._$uuid_count++;
    this._queues.push({
      uuid,
      callbacks: [callback],
      params,
    });
    return uuid;
  }

  pushMulti(params: any, ...callbacks: AsyncCallback[]) {
    const uuid = AsyncQueue._$uuid_count++;
    this._queues.push({
      uuid,
      callbacks,
      params,
    });
    return uuid;
  }

  remove(uuid: number) {
    if (this._runnungAsyncTask?.uuid === uuid) {
      console.warn("A running task cannot be removed");
      return;
    }
    for (let i = 0; i < this._queues.length; i++) {
      if (this._queues[i].uuid === uuid) {
        this._queues.splice(i, 1);
        break;
      }
    }
  }

  clear() {
    this._queues = [];
    this._isProcessingTaskUUID = 0;
    this._runnungAsyncTask = null;
  }

  play(args: any = null) {
    if (this.isProcessing) {
      return;
    }
    if (!this._enable) {
      return;
    }

    const actionData: AsyncTask = this._queues.shift() as AsyncTask;
    if (actionData) {
      this._runnungAsyncTask = actionData;
      const taskUUID = actionData.uuid;
      this._isProcessingTaskUUID = taskUUID;
      const callbacks = actionData.callbacks;

      if (callbacks.length === 1) {
        const nextFunc = (nextArgs: any = null) =>
          this.next(taskUUID, nextArgs);
        callbacks[0](nextFunc, actionData.params, args);
      } else {
        let funm = callbacks.length;
        const nextArgsArr: any[] = [];
        const nextFunc = (nextArgs: any = null) => {
          --funm;
          nextArgsArr.push(nextArgs || null);
          if (funm === 0) {
            this.next(taskUUID, nextArgsArr);
          }
        };
        const knum = funm;
        for (let i = 0; i < knum; i++) {
          callbacks[i](nextFunc, actionData.params, args);
        }
      }
    } else {
      this._isProcessingTaskUUID = 0;
      this._runnungAsyncTask = null;
      if (this.complete) {
        this.complete(args);
      }
    }
  }

  protected next(taskUUID: number, args: any = null) {
    if (this._isProcessingTaskUUID === taskUUID) {
      this._isProcessingTaskUUID = 0;
      this._runnungAsyncTask = null;
      this.play(args);
    }
  }
}

export default AsyncQueue;

const queue = new AsyncQueue();
queue.push((next, params) => {
  console.log("执行任务1");
  setTimeout(() => {
    console.log("任务1完成");
    next();
  }, 1000);
});
queue.push((next, params) => {
  console.log("执行任务2");
  setTimeout(() => {
    console.log("任务2完成");
    next();
  }, 1000);
});
queue.complete = () => console.log("执行完毕");
queue.play(); // 执行任务1 -> 执行任务2 -> 执行完毕
